#include "view.h"

#include <vector>
#include <iostream>
#include "model.h"
#include "movableobject.h"
#include "util/cvector.h"
#include "gameconsole.h"

using namespace spacedefender;
//using namespace Ogre;

//class spacedefender::MovableObject2;

View::View(Model* model)
{
	_model = model;
	_robotNode = 0;
	_sceneMgr = 0;
	_root = 0;
	_window = 0;
	_rollNode = 0;
	_pitchNode = 0;
	_yawNode = 0;
	_cameraNode = 0;
	_debugOverlay = 0;
	_gameConsole = 0;
}

View::~View()
{
	if(_gameConsole)
	{
		_gameConsole->Shutdown();
	}
	
	if(_model)
	{
		_model = 0;
	}
	
	_rollNode = 0;
	_pitchNode = 0;
	_yawNode = 0;
	_cameraNode = 0;
	
	if(_sceneMgr)
	{
		_sceneMgr->clearScene();
		_sceneMgr->destroyAllCameras();
		_root->destroySceneManager(_sceneMgr);
	}
	
	if(_window)
	{
		_window->removeAllViewports();
	}
	
	if(_root)
	{
		
		delete _root;
		_root = 0;
	}
}

bool View::Update()
{
	//this renders one frame of the game. 
	//At the start of the function call it calls all frame listeners "frameStarted" function
	return _root->renderOneFrame();
}

Ogre::RenderWindow* View::GetWindow() const
{
	return _window;
}

bool View::Initialize()
{
	_root = new Ogre::Root();

	// add resource locations
	AddResourceLocations();

	// if we cannot initialise Ogre, just abandon the whole deal
	if ( !InitOgreCore() ) return false;

	CreateSceneManager();
	CreateCamera();
	CreateViewports();

	// Set default mipmap level (NB some APIs ignore this)
	Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);

	// Create any resource listeners (for loading screens)
	CreateResourceListener();

	// Initialise resources
	InitResources();

	//SetupInputSystem();

	
	this->AddFrameListener(this);

	// Create the scene
	CreateScene();

	//CreateFrameListener();

	_gameConsole = GameConsole::GetInstance();
	_gameConsole->Init(_root);
//	_gameConsole->AddCommand("clear", &  GameConsole::ClearConsole);
	
   return true;
}

void View::AddResourceLocations()
{
   // Load resource paths from config file
   Ogre::ConfigFile cf;
   cf.load("resources.cfg");

   // Go through all sections & settings in the file
   Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

  Ogre::String secName, typeName, archName;
   while (seci.hasMoreElements())
   {
      secName = seci.peekNextKey();
      Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
      Ogre::ConfigFile::SettingsMultiMap::iterator i;
      for (i = settings->begin(); i != settings->end(); ++i)
      {
         typeName = i->first;
         archName = i->second;
         Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
      }
   }
}

bool View::InitOgreCore()
{
   // Show the configuration dialog and initialise the system
   // You can skip this and use root.restoreConfig() to load configuration
   // settings if you were sure there are valid ones saved in ogre.cfg
   if(_root->restoreConfig() || _root->showConfigDialog())
   {
      // If returned true, user clicked OK so initialise
      // Here we choose to let the system create a default rendering window by passing 'true'
      _window = _root->initialise(true);
      return true;
   }
   else
   {
      return false;
   }
}

void View::CreateSceneManager()
{
   // Create the SceneManager, in this case a generic one
   _sceneMgr = _root->createSceneManager(Ogre::ST_GENERIC);
}

void View::CreateCamera()
{
	// Create the camera
	_camera = _sceneMgr->createCamera("PlayerCam");
	
	// Create the camera's top node (which will only handle position).
	_cameraNode = _sceneMgr->getRootSceneNode()->createChildSceneNode();
	_cameraNode->setPosition(50, 50, 200);

	// Create the camera's yaw node as a child of camera's top node.
	_yawNode = _cameraNode->createChildSceneNode();
	
	// Create the camera's pitch node as a child of camera's yaw node.
	_pitchNode = _yawNode->createChildSceneNode();
	
	// Create the camera's roll node as a child of camera's pitch node
	// and attach the camera to it.
	_rollNode = _pitchNode->createChildSceneNode();
	_rollNode->attachObject(_camera);
	
	// Look back along -Z
	//_camera->lookAt(Ogre::Vector3(1,1,-300));
	_camera->setNearClipDistance(5);
}

void View::CreateViewports()
{
   // Create one viewport, entire window
   Ogre::Viewport* vp = _window->addViewport(_camera);
   vp->setBackgroundColour(Ogre::ColourValue(0,0,0));

   // Alter the camera aspect ratio to match the viewport
   _camera->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
}

void View::CreateResourceListener()
{

}

void View::InitResources()
{
   // Initialise, parse scripts etc
   Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}


void View::CreateScene()
{
	_sceneMgr->setAmbientLight( Ogre::ColourValue( 1, 1, 1 ) );
	
	const std::vector<MovableObject*> movableObjects = _model->GetObjects();
	
	Ogre::Entity* ent = 0;
	Ogre::SceneNode* sceneNode = 0;
	
	for(std::vector<MovableObject*>::const_iterator it = movableObjects.begin(); it != movableObjects.end(); it++)
	{
		MovableObject* obj = *it;
		const int iobjectId  = obj->GetObjectId();
		Ogre::String objectId = Ogre::StringConverter::toString(iobjectId);
		Ogre::String objectType = obj->GetObjectType() + ".mesh";
		ent = _sceneMgr->createEntity( objectId , objectType );
		sceneNode = _sceneMgr->getRootSceneNode()->createChildSceneNode( objectId );
		sceneNode->attachObject(ent);
		spacedefender::Vector objPos = obj->GetPosition();
		sceneNode->setPosition(Ogre::Vector3(objPos.i, objPos.j, objPos.k));
	}
	
	
	/*Entity *ent1 = _sceneMgr->createEntity( "LambdaShip", "MeteoriteSkin.mesh" );
	
	_robotNode = _sceneMgr->getRootSceneNode()->createChildSceneNode( "PlayerShip" );
	_robotNode->attachObject( ent1 );
	_robotNode->scale(5, 5, 5);*/
	
	if(!_debugOverlay)
		_debugOverlay = Ogre::OverlayManager::getSingleton().getByName("Core/DebugOverlay");
		
	_debugOverlay->show();
	
	//set the camera to the players ship position
	spacedefender::Vector shipPos =_model->GetPlayerShipPosition();
	_cameraNode->setPosition(Ogre::Vector3(shipPos.i, shipPos.j, shipPos.k));
	//spacedefender::Vector shipDir =_model->GetPlayerShipDirection();
	//_cameraNode->setOrientation(
}

void View::DestroyScene()
{
	_debugOverlay->hide();
}

void View::AddFrameListener(Ogre::FrameListener* listener)
{
	_root->addFrameListener(listener);
}

void View::Yaw(Ogre::Radian angle)
{
	_yawNode->yaw(angle);
}

void View::Roll(Ogre::Radian angle)
{
	_rollNode->roll(angle);
}

void View::Pitch(Ogre::Radian angle)
{
	_pitchNode->pitch(angle);
}

void View::UpdateStats()
{
	static Ogre::String currFps = "Current FPS: ";
	static Ogre::String avgFps = "Average FPS: ";
	static Ogre::String bestFps = "Best FPS: ";
	static Ogre::String worstFps = "Worst FPS: ";
	static Ogre::String tris = "Triangle Count: ";
	static Ogre::String batches = "Batch Count: ";

	// update stats when necessary
	try {
		Ogre::OverlayElement* guiAvg = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/AverageFps");
		Ogre::OverlayElement* guiCurr = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/CurrFps");
		Ogre::OverlayElement* guiBest = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/BestFps");
		Ogre::OverlayElement* guiWorst = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/WorstFps");

		const Ogre::RenderTarget::FrameStats& stats = _root->getAutoCreatedWindow()->getStatistics();
		guiAvg->setCaption(avgFps + Ogre::StringConverter::toString(stats.avgFPS));
		guiCurr->setCaption(currFps + Ogre::StringConverter::toString(stats.lastFPS));
		guiBest->setCaption(bestFps + Ogre::StringConverter::toString(stats.bestFPS)
			+" "+ Ogre::StringConverter::toString(stats.bestFrameTime)+" ms");
		guiWorst->setCaption(worstFps + Ogre::StringConverter::toString(stats.worstFPS)
			+" "+ Ogre::StringConverter::toString(stats.worstFrameTime)+" ms");

		Ogre::OverlayElement* guiTris = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/NumTris");
		guiTris->setCaption(tris + Ogre::StringConverter::toString(stats.triangleCount));

		Ogre::OverlayElement* guiBatches = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/NumBatches");
		guiBatches->setCaption(batches + Ogre::StringConverter::toString(stats.batchCount));

		Ogre::OverlayElement* guiDbg = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/DebugText");
		Ogre::Quaternion cameraOrientation = _camera->getOrientation();
		guiDbg->setCaption("(" + Ogre::StringConverter::toString( cameraOrientation.w) + ", " + Ogre::StringConverter::toString( cameraOrientation.x) + ", " + Ogre::StringConverter::toString( cameraOrientation.y) + ", " + Ogre::StringConverter::toString( cameraOrientation.z) + ")");
	}
	catch(...) {  }
}

bool View::frameStarted(const Ogre::FrameEvent& evt)
{	
	UpdateStats();
	
	Vector v ;//= _model->GetPlayerShipDirection();
	const Quaternion q = _model->GetPlayerShip()->GetDirectionQuat();
//	std::cout << "v=" << v.ToString();
	v = _model->GetPlayerShip()->GetPosition();
	Ogre::Vector3 v3(v.i,v.j,v.k);
	_cameraNode->setPosition(v3);
	//_camera->move (v3);
	_camera->setDirection(q._x,q._y,q._z );
	//_camera->setDirection( Ogre::Vector3(v.i, v.j , v.k ));

	_gameConsole->update(evt);

	return true;
}

bool View::frameEnded(const Ogre::FrameEvent& evt)
{
   return true;
}

GameConsole* View::GetConsole() const
{
	return _gameConsole;
}
